/*INCLUDES *******************************************************************/
#include "ses_timer.h"
#include "ses_scheduler.h"

/* CONSTANT DEFINITIONS ******************************************************/
#define MILLI_REMAINDER 1000
#define SEC_REMAINDER 60
#define MIN_REMAINDER 60
#define HOUR_REMAINDER 24
#define MILLI_TO_SEC 1000
#define SEC_TO_MIN 60
#define MIN_TO_HOUR 60
#define SEC_TO_MILLI 1000
#define MIN_TO_SEC 60
#define HOUR_TO_MIN 60

/* TYPES *********************************************************************/
typedef struct {
	task_t task;
	uint16_t expire;
} taskDescriptor_t;

/* PRIVATE VARIABLES *********************************************************/
volatile taskDescriptor_t tasks[SCHEDULER_ENTRIES];
uint32_t currentTime;

/*FUNCTION DEFINITION ********************************************************/

void scheduler_getTimeRTC(type_timeRTC *time) {
	uint32_t tempCurrentTime;

	//check if time is set
	if (time == NULL)
		return;

	cli();
	tempCurrentTime = currentTime;
	sei();

	time->milli = (tempCurrentTime % MILLI_REMAINDER );
	time->second = (tempCurrentTime / MILLI_TO_SEC ) % SEC_REMAINDER;
	time->minute = (tempCurrentTime / MILLI_TO_SEC / SEC_TO_MIN) % MIN_REMAINDER;
	time->hour = (tempCurrentTime / MILLI_TO_SEC / SEC_TO_MIN / MIN_TO_HOUR) % HOUR_REMAINDER;
	/*time->milli = (uint8_t)(tempCurrentTime % MILLI_REMAINDER);
	time->second = (uint8_t)(tempCurrentTime / MILLI_TO_SEC) % SEC_REMAINDER;
	time->minute = (uint8_t) (tempCurrentTime / MILLI_TO_MIN) % MIN_REMAINDER;
	time->hour = (uint8_t) (tempCurrentTime / MILLI_TO_HOUR) % HOUR_REMAINDER;*/
}

void scheduler_setTimeRTC(const type_timeRTC *time) {
	uint32_t tempCurrentTime = 0;

	//check if time is set
	if (time == NULL)
		return;
	if ((time->hour > 24) || (time->minute > 60) || (time->second > 60)
			|| (time->milli > 1000))
		return;

	tempCurrentTime += ((uint32_t)time->milli);
	tempCurrentTime += ((uint32_t)time->second) * SEC_TO_MILLI;
	tempCurrentTime += ((uint32_t)time->minute) *  MILLI_TO_SEC * MIN_TO_SEC;
	tempCurrentTime += ((uint32_t)time->hour) *  MILLI_TO_SEC * MIN_TO_SEC * HOUR_TO_MIN;
	cli();
	currentTime = tempCurrentTime;
	sei();

}

uint32_t scheduler_getTime() {
	uint32_t returnTime;

	cli();
	returnTime = currentTime;
	sei();

	return returnTime;
}

void scheduler_setTime(uint32_t time) {

	cli();
	currentTime = time;
	sei();
}

/**
 * Updates the tasks in the scheduler every ms.
 * Increases clock counter by 1.
 */
void scheduler_update(void) {

	currentTime++;

	for (int i = 0; i < SCHEDULER_ENTRIES; i++) {
		if (tasks[i].task == EMPTY_TASK)
			continue;

		if (tasks[i].expire == 0)
			continue;

		tasks[i].expire -= 1;
	}
}

void scheduler_init() {
	timer2_init(scheduler_update);

	currentTime = 0;

	for (int i = 0; i < SCHEDULER_ENTRIES; i++) {
		tasks[i].task = EMPTY_TASK;
	}

}

void scheduler_run() {
	task_t temp_task = EMPTY_TASK;
	while (1) {
		for (int i = 0; i < SCHEDULER_ENTRIES; i++) {
			cli();
			//check if there is an task and if it should be executed
			if (tasks[i].task == EMPTY_TASK || tasks[i].expire != 0) {
				sei();
				continue;
			}
			temp_task = tasks[i].task;
			tasks[i].task = EMPTY_TASK;
			sei();
			temp_task();
		}
	}
}

bool scheduler_add(task_t task, uint16_t expire) {
	for (int i = 0; i < SCHEDULER_ENTRIES; i++) {
		if (tasks[i].task == EMPTY_TASK) {
			tasks[i].task = task;
			tasks[i].expire = expire;
			return true;
		}
	}
	return false;
}

void scheduler_remove(task_t task) {
	for (int i = 0; i < SCHEDULER_ENTRIES; i++) {
		if (tasks[i].task == task) {
			tasks[i].task = EMPTY_TASK;
			tasks[i].expire = EXPIRE_STD_TIME;
			return;
		}
	}
}

